package org.example.hrpc.config.service;

import org.example.hrpc.common.URL;
import org.example.hrpc.common.constant.CommonConstant;
import org.example.hrpc.common.spi.loader.ExtensionLoader;
import org.example.hrpc.common.utils.CollectionUtils;
import org.example.hrpc.common.utils.StringUtils;
import org.example.hrpc.config.RegistryConfig;
import org.example.hrpc.registry.Registry;
import org.example.hrpc.rpc.core.Exporter;
import org.example.hrpc.rpc.core.Invoker;
import org.example.hrpc.rpc.core.Protocol;
import org.example.hrpc.rpc.core.ProxyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class ServiceConfig<T> extends ServiceConfigBase<T> {
    private static final Logger logger = LoggerFactory.getLogger(ServiceConfig.class);
    final static  String  protoclName = "hrpc";
    /**
     * A {@link ProxyFactory} implementation that will generate a exported service proxy,the JavassistProxyFactory is its
     * default implementation
     */
    private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("jdk");

    private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("registry");
    /**
     * Whether the provider has been exported
     */
    private transient volatile boolean exported;

    /**
     * The flag whether a service has unexported ,if the method unexported is invoked, the value is true
     */
    private transient volatile boolean unexported;

    /**
     * The exported services
     */
    private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();

    /**
     * The registry list the service will register to
     */
    protected List<RegistryConfig> registries;

    public synchronized void export() {
        doExport();
    }


    protected synchronized void doExport() {
        if(unexported){
            throw new RuntimeException();
        }
        if(exported) {
            return;
        }
        exported = true;
        if(StringUtils.isEmpty(interfaceName)) {
            if(Objects.isNull(interfaceClass)) {
                throw new IllegalStateException("Error initialized interfaceClass or interfaceName parameters");
            }
            interfaceName = interfaceClass.getName();
        }
        if(StringUtils.isEmpty(path)) {
            path = interfaceName;
        }

        doExportUrls();

    }

    private void doExportUrls() {
        // 加载注册中心链接
        // 有可能存在多注册中心
        List<URL> registryUrls = loadRegistries();
        logger.info("load registry {} urls: {}", registryUrls.size(), registryUrls);
        // 遍历Protocols，并在每个协议下导出服务
        doExportUrlsForProtocol(registryUrls);
    }

    private void doExportUrlsForProtocol(List<URL> registryUrls) {
        // 是否是泛化调用
        // 这里和简化了许多，dubbo的流程这里很复杂
        // 这里也不讨论 泛化调用
        // 直接 将interfaceName作为 serviceKey

        // 存在注册中心，导出到远程
        if(registryUrls != null && !registryUrls.isEmpty()) {
            for(URL registryURL : registryUrls) {
                // 为服务提供类ref生成Invoker
                Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL);
                Exporter<?> exporter = PROTOCOL.export(invoker);
                exporters.add(exporter);
            }
        }
        // 不存在注册中心，服务直连， 仅导出服务
        else {
            URL url = new URL(protoclName, socketAddress.getHostName(), socketAddress.getPort(),
                    interfaceName, null);
            Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
            Exporter<?> exporter = PROTOCOL.export(invoker);
            exporters.add(exporter);
        }

    }

    /**
     * 虽然这么写，但是目前仅支持 单注册中心，List.sieze()==1
     * @return
     */
    private List<URL> loadRegistries() {
        List<URL> registryList = new ArrayList<>();

        if(CollectionUtils.isNotEmpty(registries)) {

            for(RegistryConfig config : registries) {
                String[] parameters = new String[]{"version=1.0", "register=true", "host=" + config.getAddress(), "port="+config.getPort()};
                URL url = new URL(config.getProtocol(), config.getUsername(), config.getPassword(),
                        socketAddress.getHostName(), socketAddress.getPort(),
                        interfaceName, null, null, parameters);
                registryList.add(url);
            }
        }
        return registryList;
    }


    public void addRegistry(RegistryConfig registryConfig) {
        if(registries == null) {
            registries = new ArrayList<>();
        }
        registries.add(registryConfig);
    }

}
